package xfuzzy.xfhl.model;

import xfuzzy.lang.Specification;
import xfuzzy.lang.XflException;
import xfuzzy.xfhl.model.hierStruct.XfhlHierStruct;
import xfuzzy.xfhl.model.hierStruct.XfhlSingleHierStruct;
import xfuzzy.xfhl.model.spec.XfhlSpecification;
import xfuzzy.xfsl.model.XfslAlgorithm;
import xfuzzy.xfsl.model.XfslAlgorithmFactory;
import xfuzzy.xfsl.model.XfslConfig;
import xfuzzy.xfsl.model.XfslEndCondition;
import xfuzzy.xfsl.model.XfslErrorFunction;
import xfuzzy.xfsl.model.XfslThread;
import xfuzzy.xfsl.model.algorithm.Marquardt;
import xfuzzy.xfsl.model.algorithm.RProp;
import xfuzzy.xfsl.model.algorithm.XfslGradientBasedAlgorithm;

/**
 * Hebra que obtiene una estructura, la ajusta y calcula su error cuadratico
 * medio
 * 
 * @author Sergio Cala Cordero, Septiembre 2009, Herramienta XFHL
 * 
 */

public class XfhlThread extends Thread {

	/**
	 * Identificador de la hebra
	 */
	private int idThread;

	/**
	 * Configuracion del modulo Xfhl
	 */
	private XfhlConfig confXfhl;

	/**
	 * Instancia del xfhlData que va a utilizar la hebra
	 */
	private XfhlData data;

	/**
	 * Constructor
	 * 
	 * @param idThread
	 *            Identificador de la Hebra
	 * @param conf
	 *            Configuracion de la herramienta XfhlConfig
	 * @param data
	 *            XfhlData que utilizar� la hebra
	 */
	public XfhlThread(int idThread, XfhlConfig conf, XfhlData data) {
		this.idThread = idThread;
		this.confXfhl = conf;
		this.data = data;
	}

	/**
	 * Ejecucion de la hebra
	 */
	public void run() {

		// Obtenemos una instancia del singlenton
		XfhlSingleHierStruct singleHier = XfhlSingleHierStruct.getInstance();
		if (singleHier != null) {
			// Declaramos una estructura jerarquica
			XfhlHierStruct struc;

			// Mientras que haya estructuras
			while ((struc = singleHier.getXfhlHierStruct()) != null) {

				
				// Creamos la especificacion
				Specification spec = null;

				try {

					spec = XfhlSpecification.create(struc, this.confXfhl);

				} catch (XflException e) {
					e.printStackTrace();
				}

				// Ajustamos la especificacion
				this.data.setSpec(spec);
				learning(spec);
				// El ajuste lo tenemos en spec
				// Aqui en teoria el hilo ha acabado su ejecucion y se debe salvar en un fichero su
				// codificacion y su valor de fitness
				
			}
		}
		System.out.println("Soy la hebra " + this.idThread + " y he terminado.");
	}

	/**
	 * Ajusta las salidas del sistema con el algoritmo aprendizaje
	 */
	private void learning(Specification spec) {

		XfslThread lrnthread = new XfslThread(spec, createtXfslConfig());

		// Añadimos al objeto data como observador para notificarle los cambios
		lrnthread.addObserver(this.data);

		try {
			lrnthread.tuning();
		} 
		catch (XflException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Crea una configuracion para la herramienta de ajuste con la configuracion
	 * de la herramienta Xfhl
	 * 
	 * @return La configuracion de la herramienta Xfsl
	 */
	private XfslConfig createtXfslConfig() {
		XfslConfig xfslConfig = new XfslConfig();

		// Parametros del algoritmo
		double param[] = { this.confXfhl.getIniUpd(), this.confXfhl.getIncFact(), this.confXfhl.getDecFact() };

		int alg = -1;
		int err = XfslErrorFunction.MEAN_SQUARE_ERROR;
		int end = XfslEndCondition.EPOCH;

		if (this.confXfhl.getAlgorithm() == 0) {
			alg = XfslAlgorithm.RPROP;

			RProp rprop;
			try {
				rprop = (RProp) XfslAlgorithmFactory.createAlgorithm(alg, param);
				// rprop.setOption(RProp.COMPUTE,
				// this.confXfhl.getPerturbation());
				rprop.setOption(RProp.COMPUTE,
						XfslGradientBasedAlgorithm.COMPUTE);
				xfslConfig = new XfslConfig();
				xfslConfig.trainingfile = this.confXfhl.getDataSetFile();
				xfslConfig.algorithm = rprop;
				xfslConfig.errorfunction = new XfslErrorFunction(err);
				xfslConfig.endcondition.setLimit(end, this.confXfhl.getnumIterAjust());

			} 
			catch (XflException e) {
				e.printStackTrace();
			}
		} else if (this.confXfhl.getAlgorithm() == 1) {

			alg = XfslAlgorithm.MARQUARDT;
			Marquardt marq;
			try {
				marq = (Marquardt) XfslAlgorithmFactory.createAlgorithm(alg, param);
				// marq.setOption(Marquardt.COMPUTE,
				// this.confXfhl.getPerturbation());
				marq.setOption(Marquardt.COMPUTE, 0);
				xfslConfig = new XfslConfig();
				xfslConfig.trainingfile = this.confXfhl.getDataSetFile();
				xfslConfig.algorithm = marq;
				xfslConfig.errorfunction = new XfslErrorFunction(err);
				xfslConfig.endcondition.setLimit(end,
						this.confXfhl.getnumIterAjust());

			} 
			catch (XflException e) {
				e.printStackTrace();
			}
		}

		return xfslConfig;
	}

	/**
	 * GETTERS AND SETTERS
	 * */
	public XfhlData getData() {
		return this.data;
	}

}
